package org.testis.testdriver;

import org.testis.testdriver.AtofEnumaration.AtofLogLevel;
import org.testis.testdriver.exceptions.TestAbortException;
import org.testis.utils.AtofDebuggingInputProvider;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


/**
 * An abstract class of the Factory Design Pattern for various Execution Methods
 * such as driving Debugging, Failed Debugging or NormalRun TestParts.
 * @author barisergun
 */
public abstract class ExecutionDriver
{

    public static InputStreamReader isr = new InputStreamReader(System.in);
    public static BufferedReader input = new BufferedReader(isr);
    protected String m_containerLogId;
    private NotificationSubjectSingleton m_subject =
        NotificationSubjectSingleton.getInstance();


    public ExecutionDriver()
    {
    }


    /**
     * This method is used to drive the TestPart in subject. 
     * 
     * @param curTestPart TestPart object in subject.
     * @param innerIt reference to the TestIterator to the child TestParts 
     * of the parent of the TestPart in subject.
     * @return boolean The result of the driving operation
     */
    public abstract boolean drive(TestPart curTestPart,
                                  TestIterator innerIt) throws
        InterruptedException;


    /**
     * This method is used to validate the TestCase in subjects result table. 
     * 
     * @param curTestCase TestCase object in subject.
     * @param inner reference to the TestIterator to the child TestParts 
     * of the parent of the TestCase in subject.
     * @return boolean The boolean value that indicates wheter validation 
     * is done or not.
     */
    public abstract boolean validate(TestPart curTestCase,
                                     TestIterator inner);


    /**
     * This method is used to wait for input from System.in from the user. 
     *      
     * @return String returns the last line from the Console.
     */
    private String readConsoleEntry() throws IOException
    {
        System.out.println("(A)bort, (R)etry or (C)ontinue: ");
        m_subject.notifyUsers();
        String line = null;


        if (NotificationSubjectSingleton.getInstance().isConsoleInputNeeded()) {
            if (!input.ready()) {
                isr = new InputStreamReader(System.in);
                input = new BufferedReader(isr);
            }
            // Read and print lines in a loop.
            // Terminate with control-Z (Windows) or control-D (other)

            line = new String(input.readLine());

            while (!(line.equals("A") || line.equals("R") || line.equals("C"))) {
                System.out.println(line + " is not a valid selection");
                System.out.println("(A)bort, (R)etry or (C)ontinue: ");
                line = new String(input.readLine());
            }

        }
        else {
            line = AtofDebuggingInputProvider.getInstance().
                getInput();


            while (!("A".equals(line) || "R".equals(line) || "C".equals(line))) {
                if (line != null) {
                    System.out.println(line + " is not a valid selection");
                    System.out.println("(A)bort, (R)etry or (C)ontinue: ");
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                }
                line = AtofDebuggingInputProvider.getInstance().
                    getInput();
            }
        }

        return line;
    }


    /**
     * This method is used to continue the Test according to user input after 
     * driving the test.
     *      
     * @param curTestPart TestPart object in subject.
     * @param innerIt reference to the TestIterator to the child TestParts 
     * of the parent of the TestPart in subject.
     * @param testPartResult boolean result of the TestPart executed
     *  
     */
    public void driveCommon(TestPart curTestPart,
                            TestIterator innerIt,
                            boolean testPartResult)
    {

        StepContainer curStepCont = (StepContainer) curTestPart;

        if (curStepCont.getContainerType() ==
            AtofEnumaration.TestPartType.TestCaseGroup ||
            curStepCont.getContainerType() ==
            AtofEnumaration.TestPartType.TestPlan) {

            System.out.println(curTestPart.getPartName() + " is validated as " +
                testPartResult);

            String ht = "";
            try {
                ht = this.readConsoleEntry();
            } catch (IOException ex) {

                System.out.println(ex.getMessage());
            }

            if (ht.equals("R")) {
                innerIt.previous();
                testPartResult = true; // because retry selected return result as true
                NotificationSubjectSingleton.getInstance().notifyLogMessages
                                                    (curTestPart.getPartName(),
                                                   AtofLogLevel.INFO,
                                                   "User choosed to retry the TestPart",
                                                   curTestPart.m_partGuid);

            }
            else if (ht.equals("A")) {
                throw new TestAbortException("Test abort requested on " +
                    curTestPart.getPartName());
            }
            else if (ht.equals("C")) {
                NotificationSubjectSingleton.getInstance().notifyLogMessages(curTestPart.getPartName(),
                                                   AtofLogLevel.INFO,
                                                   "User choosed to continue to the next TestPart",
                                                   curTestPart.m_partGuid);
            }
        }
        



    }


    /**
     * This method is used to contiue the Test according to user input after 
     * validating the test.
     *      
     * @param curTestCase TestCase object in subject.
     * @param inner reference to the TestIterator to the child TestParts 
     * of the parent of the TestCase in subject.
     * @param testPartResult boolean result of the TestPart executed
     *  
     */
    public boolean validateCommon(TestPart curTestCase,
                                  TestIterator inner,
                                  boolean testPartResult)
    {

        if (curTestCase.getContainerType() !=
            AtofEnumaration.TestPartType.TestStepGroup) {
            boolean setCaseResult = false;
            StepContainer curCase = (StepContainer) curTestCase;
            System.out.println(curCase.getPartName() +
                " is validated as " +
                testPartResult);

            String ht = "";
            try {
                ht = this.readConsoleEntry();
            } catch (IOException ex) {
                System.out.println(ex.getMessage());
            }

            if (ht.equals("R")) {
                inner.previous();
                NotificationSubjectSingleton.getInstance().notifyLogMessages(curTestCase.getPartName(),
                                                   AtofLogLevel.INFO,
                                                   "User choosed to retry the TestPart",
                                                   curTestCase.m_partGuid);
            }
            else if (ht.equals("A")) {
                throw new TestAbortException("Test abort requested on " +
                    curCase.getPartName());
            }
            else if (ht.equals("C")) {
                NotificationSubjectSingleton.getInstance().notifyLogMessages(curTestCase.getPartName(),
                                                   AtofLogLevel.INFO,
                                                   "User choosed to continue to the next TestPart",
                                                   curTestCase.m_partGuid);
                setCaseResult = true;
            }

            return setCaseResult;
        }
        else {
            return true; // if the container is Test Step Group there is no need
            // to Debug it with various modes.
        }
    }
}
